package com.lin.filegraph.utils.parser.simpleast;

import com.lin.filegraph.utils.compdg.ComponentNode;
import com.lin.filegraph.utils.parser.node.*;

import java.util.ArrayList;
import java.util.List;

/**
 * @description:
 * @author: linhuaixu
 * @time: 2023/5/18 10:16
 */

public class SimpleASTHelper {
    public static JDirectoryNode getDirByName(String strDirName, JProjectNode root) {
        for (JDirectoryNode dir : root.getAllDirectories()) {
            if (dir.getStrDirName().equals(strDirName)) {
                return dir;
            }
        }
        return null;
    }

    public static JPackageNode getPkgByName(String strPkgName, JDirectoryNode dir) {
        for (JPackageNode pkg : dir.getAllPackages()) {
            if (pkg.getPackageName().equals(strPkgName)) {
                return pkg;
            }
        }
        return null;
    }

    public static JPackageNode getPkgByDirPath(String strDirPath, JProjectNode root) {
        for (JPackageNode pkg : findAllPackagesInSubTree(root)) {
            if (pkg.getFilePath().equals(strDirPath) || pkg.getRelativePath().equals(strDirPath)) {
                return pkg;
            }
        }
        return null;
    }

    public static List<JPackageNode> getPkgListByName(String strPkgName, JProjectNode root) {
        List<JPackageNode> pkgList = new ArrayList<>();
        for (JPackageNode pkg : findAllPackagesInSubTree(root)) {
            if (pkg.getPackageName().equals(strPkgName)) {
                pkgList.add(pkg);
            }
        }
        return pkgList;
    }

    public static List<JPackageNode> getListPkgsByName(String strPkgName, JProjectNode root) {
        List<JPackageNode> listOfPkgs = new ArrayList<>();
        for (JPackageNode pkg : findAllPackagesInSubTree(root)) {
            if (pkg.getPackageName().equals(strPkgName)) {
                listOfPkgs.add(pkg);
            }
        }
        return listOfPkgs;
    }

    public static boolean isPkgExist(String strPkgName, JProjectNode root) {
        for (JPackageNode pkg : findAllPackagesInSubTree(root)) {
            if (pkg.getPackageName().equals(strPkgName)) {
                return true;
            }
        }
        return false;
    }

    public static JFileNode getFileByPath(String strFilePath, JProjectNode root) {
        for (JFileNode fileNode : findAllFilesInSubTree(root)) {
            if (fileNode.getFilePath().equals(strFilePath) || fileNode.getRelativePath().equals(strFilePath)) {
                return fileNode;
            }
        }
        return null;
    }

    public static boolean isTypeExist(String strTypeName, JProjectNode root) {
        // TODO Auto-generated method stub
        for (JTypeNode typeNode : findAllTypesInSubTree(root)) {
            if (typeNode.getTypeName().equals(strTypeName)) {
                return true;
            }
        }
        return false;
    }

    public static JTypeNode getTypeByName(String strTypeName, JProjectNode root) {
        for (JTypeNode typeNode : findAllTypesInSubTree(root)) {
            if (typeNode.getTypeName().equals(strTypeName)) {
                return typeNode;
            }
        }
        return null;
    }

    public static void displaySimpleAST(JProjectNode root) {
        System.out.println("Display SimpleAST...");
        System.out.println(root);
        for (JDirectoryNode dir : root.getAllDirectories()) {
            System.out.println("    " + dir);
            for (JPackageNode packageNode : dir.getAllPackages()) {
                System.out.println("        " + packageNode);
                for (JFileNode fileNode : packageNode.getAllFiles()) {
                    System.out.println("            " + fileNode);
                    for (JImportNode importNode : fileNode.getAllImports()) {
                        System.out.println("                " + importNode);
                    }
                    for (JTypeNode typeNode : fileNode.getAllTypes()) {
                        System.out.println("                " + typeNode);
                        if (typeNode instanceof JEnumNode) {
                            for (JEnumConstantNode enumConstantNode : ((JEnumNode) typeNode).getAllEnumConstants()) {
                                System.out.println("                    " + enumConstantNode);
                            }
                        }
                        for (JFieldNode fieldNode : typeNode.getAllFields()) {
                            System.out.println("                    " + fieldNode);
                        }
                        for (JMethodNode methodNode : typeNode.getAllMethods()) {
                            System.out.println("                    " + methodNode);
                            for (JParameterNode parameterNode : methodNode.getAllParameters()) {
                                System.out.println("                        " + parameterNode);
                            }
                        }
                    }
                }
            }
        }
    }

    public static void displayStrInheritance(JProjectNode root) {
        System.out.println("Display StrInheritance of classes and interfaces...");
        for (JTypeNode typeNode : SimpleASTHelper.findAllTypesInSubTree(root)) {
            typeNode.displayStrInheritance();
        }
    }

    public static List<JPackageNode> findAllPackagesInSubTree(JTreeNode node) {
        List<JPackageNode> pkgList = new ArrayList<JPackageNode>();
        if (node instanceof JProjectNode || node instanceof JDirectoryNode) {
            for (JTreeNode childNode : node.getChildrenNodeList()) {
                pkgList.addAll(findAllPackagesInSubTree(childNode));
            }
        }
        if (node instanceof JPackageNode) {
            pkgList.add((JPackageNode) node);
        }
        return pkgList;
    }

    public static List<JFileNode> findAllFilesInSubTree(JTreeNode node) {
        List<JFileNode> fileNodeList = new ArrayList<JFileNode>();
        if (node instanceof JProjectNode || node instanceof JDirectoryNode || node instanceof JPackageNode) {
            for (JTreeNode childNode : node.getChildrenNodeList()) {
                fileNodeList.addAll(findAllFilesInSubTree(childNode));
            }
        }
        if (node instanceof JFileNode) {
            fileNodeList.add((JFileNode) node);
        }
        return fileNodeList;
    }

    public static List<JTypeNode> findAllTypesInSubTree(JTreeNode node) {
        List<JTypeNode> typeNodeList = new ArrayList<JTypeNode>();
        if (node instanceof JProjectNode || node instanceof JDirectoryNode || node instanceof JPackageNode
                || node instanceof JFileNode) {
            for (JTreeNode childNode : node.getChildrenNodeList()) {
                typeNodeList.addAll(findAllTypesInSubTree(childNode));
            }
        }
        if (node instanceof JTypeNode) {
            typeNodeList.add((JTypeNode) node);
        }
        return typeNodeList;
    }

    public static List<JClassNode> findAllClassesInSubTree(JTreeNode node) {
        List<JClassNode> classNodeList = new ArrayList<JClassNode>();
        if (node instanceof JProjectNode || node instanceof JDirectoryNode || node instanceof JPackageNode
                || node instanceof JFileNode) {
            for (JTreeNode childNode : node.getChildrenNodeList()) {
                classNodeList.addAll(findAllClassesInSubTree(childNode));
            }
        }
        if (node instanceof JClassNode) {
            classNodeList.add((JClassNode) node);
        }
        return classNodeList;
    }

    public static List<JInterfaceNode> findAllInterfacesInSubTree(JTreeNode node) {
        List<JInterfaceNode> interfaceNodeList = new ArrayList<JInterfaceNode>();
        if (node instanceof JProjectNode || node instanceof JDirectoryNode || node instanceof JPackageNode
                || node instanceof JFileNode) {
            for (JTreeNode childNode : node.getChildrenNodeList()) {
                interfaceNodeList.addAll(findAllInterfacesInSubTree(childNode));
            }
        }
        if (node instanceof JInterfaceNode) {
            interfaceNodeList.add((JInterfaceNode) node);
        }
        return interfaceNodeList;
    }

    public static List<JEnumNode> findAllEnumsInSubTree(JTreeNode node) {
        List<JEnumNode> enumNodeList = new ArrayList<JEnumNode>();
        if (node instanceof JProjectNode || node instanceof JDirectoryNode || node instanceof JPackageNode
                || node instanceof JFileNode) {
            for (JTreeNode childNode : node.getChildrenNodeList()) {
                enumNodeList.addAll(findAllEnumsInSubTree(childNode));
            }
        }
        if (node instanceof JEnumNode) {
            enumNodeList.add((JEnumNode) node);
        }
        return enumNodeList;
    }

    public static List<JMethodNode> findAllMethodsInSubTree(JTreeNode node) {
        List<JMethodNode> methodNodeList = new ArrayList<JMethodNode>();
        if (node instanceof JProjectNode || node instanceof JDirectoryNode || node instanceof JPackageNode
                || node instanceof JFileNode || node instanceof JTypeNode) {
            for (JTreeNode childNode : node.getChildrenNodeList()) {
                methodNodeList.addAll(findAllMethodsInSubTree(childNode));
            }
        }
        if (node instanceof JMethodNode) {
            methodNodeList.add((JMethodNode) node);
        }
        return methodNodeList;
    }

    public static List<JFieldNode> findAllFieldsInSubTree(JTreeNode node) {
        List<JFieldNode> fieldNodeList = new ArrayList<JFieldNode>();
        if (node instanceof JProjectNode || node instanceof JDirectoryNode || node instanceof JPackageNode
                || node instanceof JFileNode || node instanceof JTypeNode) {
            for (JTreeNode childNode : node.getChildrenNodeList()) {
                fieldNodeList.addAll(findAllFieldsInSubTree(childNode));
            }
        }
        if (node instanceof JFieldNode) {
            fieldNodeList.add((JFieldNode) node);
        }
        return fieldNodeList;
    }

    /**
     * 获得组件内所有的文件节点 。前置条件：已创建精简分析树
     *
     * @return 返回组件内所有的文件节点
     */
    public static List<JFileNode> getAllFileNodesInComp(ComponentNode comp, JProjectNode root) {
        List<JFileNode> subFileNodes = new ArrayList<>();
        for (String filePath : comp.getAllFiles()) {
            JFileNode fileNode = SimpleASTHelper.getFileByPath(filePath, root);
            if(fileNode != null) {
                subFileNodes.add(fileNode);
            }
        }
        return subFileNodes;
    }

    public static List<JMethodNode> getAllMethodsInComp(ComponentNode comp, JProjectNode root) {
        // TODO Auto-generated method stub
        List<JMethodNode> listOfMethods = new ArrayList<>();
        for (String filePath : comp.getAllFiles()) {
            JFileNode fileNode = SimpleASTHelper.getFileByPath(filePath, root);
            listOfMethods.addAll(SimpleASTHelper.findAllMethodsInSubTree(fileNode));
        }
        return listOfMethods;
    }

//    public static ComponentNode getComponentByMethod(ComponentGraph comgDG, JMethodNode methodNode, JProjectNode root) {
//        for (ComponentNode component : comgDG.getAllComponents()) {
//            if (getAllMethodsInComp(component, root).contains(methodNode)) {
//                return component;
//            }
//        }
//        return null;
//    }

    public static List<JMethodInvocationNode> findAllMethodInvocationsInSubTree(JTreeNode node) {
        List<JMethodInvocationNode> methodInvocationNodeList = new ArrayList<JMethodInvocationNode>();
        if (node instanceof JProjectNode || node instanceof JDirectoryNode || node instanceof JPackageNode
                || node instanceof JFileNode || node instanceof JTypeNode) {
            for (JTreeNode childNode : node.getChildrenNodeList()) {
                methodInvocationNodeList.addAll(findAllMethodInvocationsInSubTree(childNode));
            }
        } else if (node instanceof JMethodNode) {
            for (JMethodInvocationNode methodInvocationNode : ((JMethodNode) node).getListMethodInvokeNode()) {
                methodInvocationNodeList.add(methodInvocationNode);
            }
        }
        return methodInvocationNodeList;
    }

    public static JFileNode getFileByClsName(String clsName, JProjectNode root) {
        JTypeNode tNode = SimpleASTHelper.getTypeByName(clsName, root);
        if (tNode != null) {
            return tNode.getFile();
        } else {
            return null;
        }

    }
}
